<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .box {
            width: 200px;
            height: 200px;
            background-color: red;
        }
    </style>
</head>

<body>
    <div class="box"></div>
</body>

</body>

</html>
<script>
    //一.缓冲运动
    //1.通过定时器让一个物体运动起来。
    //2.设置缓冲速度，离目标点越远速度越大。(越来越小的速度)
    //3.封装缓冲运动 - 当前的运动只能改变left.
    //4.通过getStyle函数，让带有px单位的属性都可以运动
    //5.添加透明度
    //6.多物体运动,每一个物体给一个定时器。物体是对象，将定时器绑定在物体上面。
    //7.链式运动：前一个运动完成，开启下一次运动。通过第四个参数(可选的)来开启链式运动。
    //8.多属性同时运动：传入多个css属性进行运动改变。
    //通过一个对象将多个属性和多个属性值(目标值)通过遍历的方式进行运动。
    //等到遍历的多个属性都要到了目标点，运动才最终停止。
    //获取任意的css属性值-带单位
    function getStyle(obj, attr) { //obj:元素对象  attr:属性名称
        if (window.getComputedStyle) { //支持：标准浏览器。
            return window.getComputedStyle(obj)[attr];
        } else { //非标准，IE浏览器
            return obj.currentStyle[attr];
        }
    }
    //缓冲运动的封装。
    let speed = 0; //初始速度
    //bufferMove(this, {width: 600,height: 600, opacity: 100}, 500);  attr target
    function bufferMove(obj, json, fn) { //obj:元素对象 json:css属性名和值  fn:回调函数
        clearInterval(obj.timer); //清除前面的定时器,将定时器绑定在元素对象上面。
        obj.timer = setInterval(() => {
            let flag = 1;
            for (let attr in json) { //attr:属性名  json[attr]:目标值 target
                //1.获取改变的css属性值(px、透明度)
                let currentValue = null;
                if (attr === 'opacity') { //判断是px还是透明度
                    currentValue = Math.round(getStyle(obj, attr) * 100); //透明度,透明度的值扩大100倍
                } else {
                    currentValue = parseInt(getStyle(obj, attr)); //获取当前的属性值-带px。
                }
                //2.求速度：越来越小的速度
                speed = (json[attr] - currentValue) / 10; //速度，如果速度是整数，向上取整，否则向下取整
                speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                if (currentValue !== json[attr]) { //没到目标点，继续运动。
                    if (attr === 'opacity') { //判断是px还是透明度
                        obj.style.opacity = (currentValue + speed) / 100; //赋值时缩小100倍
                    } else {
                        obj.style[attr] = currentValue + speed + 'px'; //不断获取当前值+5再赋值
                    }
                    flag = 2;
                }
            }
            //3.判断运动是否停止
            if (flag === 1) { //如果flag=1表示for...in结束，flag不等于2，没有物体在运动。
                clearInterval(obj.timer);
                fn && typeof fn === 'function' && fn(); //走到这里，表示第一个属性的运动结束了，调用回调函数。(判断是否存在fn,而且fn一定是function,再执行此函数)
            }

        }, 1000 / 60);
    };
    const aBox = document.querySelectorAll('.box');
    aBox[0].onmouseover = function() {
        bufferMove(this, {
            width: 600,
            height: 201
        }, 500);
    };
    aBox[0].onmouseout = function() {
        bufferMove(this, {
            width: 200,
            height: 200
        }, 500);
    };
</script>